const EVENTS = {
  CLICK: 'on-node-click',
  MOUSEOUT: 'on-node-mouseout',
  MOUSEOVER: 'on-node-mouseover',
  DRAGSTART: 'on-node-drag-start',
  DRAGOVER: 'on-node-drag-over',
  DROP: 'on-node-drop'
}

const LOADING_ICON = `<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="14" height="14">
  <path
    d="M511.882596 287.998081h-0.361244a31.998984 31.998984 0 0 1-31.659415-31.977309v-0.361244c0-0.104761 0.115598-11.722364 0.115598-63.658399V96.000564a31.998984 31.998984 0 1 1 64.001581 0V192.001129c0 52.586273-0.111986 63.88237-0.119211 64.337537a32.002596 32.002596 0 0 1-31.977309 31.659415zM511.998194 959.99842a31.998984 31.998984 0 0 1-31.998984-31.998984v-96.379871c0-51.610915-0.111986-63.174332-0.115598-63.286318s0-0.242033 0-0.361243a31.998984 31.998984 0 0 1 63.997968-0.314283c0 0.455167 0.11921 11.711527 0.11921 64.034093v96.307622a31.998984 31.998984 0 0 1-32.002596 31.998984zM330.899406 363.021212a31.897836 31.897836 0 0 1-22.866739-9.612699c-0.075861-0.075861-8.207461-8.370021-44.931515-45.094076L195.198137 240.429485a31.998984 31.998984 0 0 1 45.256635-45.253022L308.336112 263.057803c37.182834 37.182834 45.090463 45.253022 45.41197 45.578141A31.998984 31.998984 0 0 1 330.899406 363.021212zM806.137421 838.11473a31.901448 31.901448 0 0 1-22.628318-9.374279L715.624151 760.859111c-36.724054-36.724054-45.018214-44.859267-45.097687-44.93874a31.998984 31.998984 0 0 1 44.77618-45.729864c0.32512 0.317895 8.395308 8.229136 45.578142 45.411969l67.88134 67.88134a31.998984 31.998984 0 0 1-22.624705 54.630914zM224.000113 838.11473a31.901448 31.901448 0 0 0 22.628317-9.374279l67.88134-67.88134c36.724054-36.724054 45.021826-44.859267 45.097688-44.93874a31.998984 31.998984 0 0 0-44.776181-45.729864c-0.32512 0.317895-8.395308 8.229136-45.578142 45.411969l-67.88134 67.884953a31.998984 31.998984 0 0 0 22.628318 54.627301zM255.948523 544.058589h-0.361244c-0.104761 0-11.722364-0.115598-63.658399-0.115598H95.942765a31.998984 31.998984 0 1 1 0-64.00158h95.996952c52.586273 0 63.88237 0.111986 64.337538 0.11921a31.998984 31.998984 0 0 1 31.659414 31.97731v0.361244a32.002596 32.002596 0 0 1-31.988146 31.659414zM767.939492 544.058589a32.002596 32.002596 0 0 1-31.995372-31.666639v-0.361244a31.998984 31.998984 0 0 1 31.659415-31.970085c0.455167 0 11.754876-0.11921 64.34115-0.11921h96.000564a31.998984 31.998984 0 0 1 0 64.00158H831.944685c-51.936034 0-63.553638 0.111986-63.665624 0.115598h-0.335957zM692.999446 363.0176a31.998984 31.998984 0 0 1-22.863126-54.381656c0.317895-0.32512 8.229136-8.395308 45.41197-45.578141l67.88134-67.884953A31.998984 31.998984 0 1 1 828.693489 240.429485l-67.892177 67.88134c-31.020013 31.023625-41.644196 41.759794-44.241539 44.393262l-0.697201 0.722488a31.908673 31.908673 0 0 1-22.863126 9.591025z"
    fill="#666" />
</svg>`

function createListener (handler, data) {
  const execute = (cb, e, ...args) => {
    if (typeof cb === 'function') {
      // fixed bug #48, #73
      const className = e.target && e.target.className
      if (typeof className === 'string' && className.indexOf('org-tree-node-btn') > -1) return

      cb.apply(null, [e, ...args])
    }
  }

  return function (e) {
    if (Array.isArray(handler)) {
      for (const cb of handler) {
        execute(cb, e, data)
      }
    } else {
      execute(handler, e, data)
    }
  }
}

// 判断是否叶子节点（即是否为树最终的节点）
const isLeaf = (data, prop) => {
  return !(Array.isArray(data[prop]) && data[prop].length > 0)
}

/**
 * 判断当前节点是否包含有子节点
 * @author yxk
 */
const hasChildren = (data, props) => {
  const { children, hasChildren } = props

  const flag = Array.isArray(data[children] && data[children].length > 0)
  if (flag) return true

  if (!hasChildren) return false
  return !!data[hasChildren]
}

// 创建 node 节点
export function renderNode(h, data, context) {
  const { props } = context
  const cls = ['org-tree-node']
  const childNodes = []
  const children = data[props.props.children]

  if (!hasChildren(data, props.props)) {
    cls.push('is-leaf')
  } else if (props.collapsable && !data[props.props.expand]) {
    cls.push('collapsed')
  }

  childNodes.push(renderLabel(h, data, context))

  if (!props.collapsable || data[props.props.expand]) {
    childNodes.push(renderChildren(h, children, context))
  }

  return h('div', {
    domProps: {
      className: cls.join(' ')
    }
  }, childNodes)
}

// 创建展开折叠按钮
export function renderBtn(h, data, context) {
  const { props, listeners } = context
  const expandHandler = listeners['on-expand']
  const loadedHandler = listeners['on-loaded']

  const lazyLoaderHandler = props.load
  const lazy = props.lazy
  let buttonClassName = props.buttonClassName

  let cls = ['org-tree-node-btn']

  if (data[props.props.expand]) {
    cls.push('expanded')
  }

  if (data.loading) {
    cls.push('loading')
  }

  if (typeof buttonClassName === 'function') {
    buttonClassName = buttonClassName(data)
  }
  buttonClassName && cls.push(buttonClassName)

  return h('span', {
    domProps: {
      className: cls.join(' '),
      innerHTML: data.loading ? LOADING_ICON : ''
    },
    on: {
      // click: (e) => expandHandler && expandHandler(e, data)
      click: (e) => {
        if (
          lazy &&
          lazyLoaderHandler &&
          typeof lazyLoaderHandler === 'function' &&
          !data.loaded
        ) {
          Object.assign(data, { loading: true })
          lazyLoaderHandler(data, function(childList) {
            Object.assign(data, {
              [props.props.children]: childList,
              hasChildren: childList.length > 0,
              loaded: true,
              loading: false,
              expand: true,
            })

            loadedHandler && loadedHandler(e, data)
          })
          return
        }

        expandHandler && expandHandler(e, data)
      }
    }
  })
}

// 创建 label 节点
export function renderLabel(h, data, context) {
  const { props, listeners } = context
  const label = data[props.props.label]
  const renderContent = props.renderContent

  // event handlers
  const clickHandler = listeners[EVENTS.CLICK]
  const mouseOutHandler = listeners[EVENTS.MOUSEOUT]
  const mouseOverHandler = listeners[EVENTS.MOUSEOVER]
  const dragStartHandler = listeners[EVENTS.DRAGSTART];
  const dragOverHandler = listeners[EVENTS.DRAGOVER];
  const dropHandler = listeners[EVENTS.DROP];

  const childNodes = []
  if (typeof renderContent === 'function') {
    let vnode = renderContent(h, data)

    vnode && childNodes.push(vnode)
  } else {
    childNodes.push(label)
  }

  if (
    props.collapsable &&
    hasChildren(data, props.props)
  ) {
    childNodes.push(renderBtn(h, data, context))
  }

  const cls = ['org-tree-node-label-inner']
  let { labelWidth, labelClassName, selectedClassName, selectedKey } = props

  if (typeof labelWidth === 'number') {
    labelWidth += 'px'
  }

  if (typeof labelClassName === 'function') {
    labelClassName = labelClassName(data)
  }

  labelClassName && cls.push(labelClassName)

  // add selected class and key from props
  if (typeof selectedClassName === 'function') {
    selectedClassName = selectedClassName(data)
  }

  selectedClassName && selectedKey && data[selectedKey] && cls.push(selectedClassName)

  return h('div', {
    domProps: {
      className: 'org-tree-node-label',
      draggable: true,
    },
    on: {
      'dragstart': createListener(dragStartHandler, data),
      'dragover': createListener(dragOverHandler, data),
      'drop': createListener(dropHandler, data)
    }
  }, [h('div', {
    domProps: {
      className: cls.join(' ')
    },
    style: { width: labelWidth },
    on: {
      'click': createListener(clickHandler, data),
      'mouseout': createListener(mouseOutHandler, data),
      'mouseover': createListener(mouseOverHandler, data),
    }
  }, childNodes)])
}

// 创建 node 子节点
export function renderChildren(h, list, context) {
  if (Array.isArray(list) && list.length) {
    const children = list.map(item => {
      return renderNode(h, item, context)
    })

    return h('div', {
      domProps: {
        className: 'org-tree-node-children'
      }
    }, children)
  }
  return ''
}

export function render(h, context) {
  const { props } = context

  return renderNode(h, props.data, context)
}

export default render
